#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const childProcess = require('child_process');

process.env.PATH = '/usr/local/bin:/opt/homebrew/bin:' + process.env.PATH;

const appPath = process.argv[2];
const appName = process.argv[3];
let winTitle = process.argv[4];
const isUpdate = process.argv.includes('--update');
const isMessage = process.argv.includes('--message');

const sessionPath = path.join(__dirname, '../session');

let session = fs.readFileSync(sessionPath).toString().replace(/\s/g, '');
process.env.BW_SESSION = session;

const passwd = childProcess.execSync('cat ~/.hammerspoon/password|openssl aes-128-cbc -d -k gh8p9bqi5vRiUf -base64').toString().trim();

let cache = '[]';

function hashCode(data) {
    let hash = 0;
    let i;
    let chr;
    if (data.length === 0) return hash;
    for (i = 0; i < data.length; i++) {
        chr = data.charCodeAt(i);
        hash = (hash << 5) - hash + chr;
        hash |= 0;
    }
    return hash;
}

function update() {
    try {
        console.log(childProcess.execSync('bw sync').toString().trim());
    } catch (e) {
        try {
            const result = childProcess.execSync('bw unlock ' + passwd).toString();
            const r1 = result.match(/export BW_SESSION="(.*?)"/);
            if (r1 && r1[1] && r1[1].length > 64) {
                session = r1[1];
                process.env.BW_SESSION = session;
                fs.writeFileSync(sessionPath, session);
            }
        } catch (e) {
            console.log('解锁失败:' + e);
            process.exit();
        }
        try {
            console.log(childProcess.execSync('bw sync').toString().trim());
        } catch (e) {
            console.log('同步失败！');
            process.exit();
        }
    }
    try {
        const newCache = childProcess.execSync('bw list items').toString().trim();
        if (newCache && newCache.length > 12) {
            try {
                const idList = JSON.parse(newCache).map((item) => item.id);
                const cacheJson = JSON.parse(cache);
                cacheJson.sort((a, b) => idList.indexOf(a.id) - idList.indexOf(b.id));
                cache = JSON.stringify(cacheJson);
            } catch (e) {
                e.message = `同化缓存失败：${e.message}`;
                throw e;
            }
            const newHash = hashCode(newCache);
            const oldHash = hashCode(cache);
            if (!cacheName || oldHash !== newHash) {
                fs.readdirSync(path.join(__dirname, '..'))
                    .filter((name) => name.startsWith('cache-'))
                    .forEach((name) => {
                        fs.unlinkSync(path.join(__dirname, '..', name));
                    });
                fs.writeFileSync(path.join(__dirname, '../cache.json'), newCache);
                childProcess.execSync(`cat ~/.hammerspoon/cache.json|openssl aes-128-cbc -k ${passwd} > ~/.hammerspoon/${'cache-' + newHash + '.json'}`);
                fs.unlinkSync(path.join(__dirname, '../cache.json'));
                console.log(childProcess.execSync('bw sync --last').toString().trim());
                console.log(`密码库已同步到最新版本，${newHash}！`);
            } else if (isMessage) {
                console.log('密码库是最新的！');
            }
        } else {
            console.log('获取项失败，没有密码！');
        }
    } catch (e) {
        console.log('获取项失败！', e);
    }
}

const cacheName = fs.readdirSync(path.join(__dirname, '..')).filter((name) => name.startsWith('cache-'))[0];
if (cacheName) {
    try {
        cache = childProcess.execSync(`cat ~/.hammerspoon/${cacheName}|openssl aes-128-cbc -d -k ${passwd}`).toString().trim();
        if (!cache) cache = '[]';
        if (!isUpdate) childProcess.exec(`osascript -e 'do shell script "node ~/.hammerspoon/script/get_passwd.js --update & exit"'`);
    } catch (e) {
        update();
        process.exit();
    }
} else {
    console.log('首次需要更新密码库！');
    update();
    console.log('请重试');
    process.exit();
}

if (isUpdate) {
    update();
    process.exit();
}

let cacheJson;
try {
    cacheJson = JSON.parse(cache);
} catch (e) {
    console.log(`解析缓存失败：`, e);
    process.exit();
}
if (!winTitle) winTitle = appName;
const fullName = appName === winTitle ? appName : `${appName}-${winTitle}`;
const fullNamePath = `${appPath}:${fullName}`;

let errorMessage = '';

function checkName(names) {
    for (let i = 0; i < names.length; i++) {
        let name = names[i];
        if (name.includes('*')) {
            name = new RegExp(`^${name.replace(/([().?{}\\])/g, '\\$1').replace(/\*/g, '.*?')}$`);
            if (name.test(fullName) || name.test(fullNamePath)) return true;
        } else if (name === fullName || name === fullNamePath || name === appName || name === winTitle) {
            return true;
        }
    }
    return false;
}

for (let i = 0; i < cacheJson.length; i++) {
    const item = cacheJson[i];
    let names = [item.name];
    let autofill = '{username}{tab}{password}{enter}';
    if (item.login && item.login.uris) {
        for (let j = 0; j < item.login.uris.length; j++) {
            const uri = item.login.uris[j];
            if (uri.uri) {
                if (uri.uri.startsWith('app://')) {
                    names.push(uri.uri.substr(6));
                }
                if (uri.uri.startsWith('autofill://')) {
                    autofill = uri.uri.substr(11);
                }
            }
        }
    }
    if (checkName(names)) {
        autofill = autofill.replace(/{(\w+)}/g, '{$1}\n');
        autofill = autofill.replace('{username}', item.login.username);
        autofill = autofill.replace('{password}', item.login.password);
        console.log(`success:${autofill}`);
        process.exit();
    }
}

console.log(`没有找到 ${appName} 的匹配项！${errorMessage}`);
process.exit();
